package tc.de.krawallmann.slick.game;

import java.util.ArrayList;
import java.util.List;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.StateBasedGame;

import tc.de.krawallmann.event.CommandKeyListener;
import tc.de.krawallmann.twl.BasicTWLGameState;
import tc.de.krawallmann.twl.RootPane;
import de.matthiasmann.twl.Widget;

/**
 * Abstract subclass of BasicGameState.<br/>
 * Provides extra functionality like:
 * <ul>
 * <li>BasicButton and component support</li>
 * <li>Focus system</li>
 * <li>InputMap support</li>
 * <li>Background images</li>
 * </ul>
 * 
 * @author KrawallMann
 * @version 1.0
 */
public abstract class AbstractMMenuState extends BasicTWLGameState implements CommandKeyListener {

	/** The background image. */
	private transient Image background;

	/** The GameContainer that contains this GameState. */
	private transient GameContainer container;

	/** The focus. */
	private transient int focus = 0;

	/** the game registered to this state. */
	private transient final AbstractExtendableGame game;

	/** The state's id. */
	private transient final int menuID;

	/** The widgets. */
	private transient List<Widget> widgets;

	/**
	 * Instantiates a new AbstractMMenuState.
	 * 
	 * @param menuID
	 *            the menu id
	 * @param game
	 *            the game
	 */
	public AbstractMMenuState(final int menuID, final AbstractExtendableGame game) {
		super();
		this.game = game;
		this.menuID = menuID;
	}

	@Override
	public void commandKeyPressed(final String event) {
		// Empty!
	}

	@Override
	public void commandKeyReleased(final String event) {
		// Empty!
	}

	/**
	 * Controller button was pressed.
	 * 
	 * @param controller
	 *            the controller's id
	 * @param button
	 *            the button's id
	 */
	@Override
	public void controllerButtonPressed(final int controller, final int button) {
		super.controllerButtonPressed(controller, button);
		// Not supported yet!
	}

	/**
	 * Controller button was released.
	 * 
	 * @param controller
	 *            the controller's id
	 * @param button
	 *            the button's id
	 */
	@Override
	public void controllerButtonReleased(final int controller, final int button) {
		super.controllerButtonReleased(controller, button);
		// Not supported yet!
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.twl.BasicTWLGameState#enter(org.newdawn.slick.
	 * GameContainer, org.newdawn.slick.state.StateBasedGame)
	 */
	@Override
	public void enter(final GameContainer container, final StateBasedGame game)
			throws SlickException {
		super.enter(container, game);
		focus = 0;
		calculateFocus();
	}

	/**
	 * Gets the background.
	 * 
	 * @return the background
	 */
	public Image getBackground() {
		return background;
	}

	/**
	 * Gets the GameContainer that contains this GameState.
	 * 
	 * @return the container that contains this GameState
	 */
	public GameContainer getContainer() {
		return container;
	}

	/**
	 * Gets the game.
	 * 
	 * @return the game
	 */
	public AbstractExtendableGame getGame() {
		return game;
	}

	/**
	 * Gets the state's id.
	 * 
	 * @return the state's id
	 */
	@Override
	public int getID() {
		return menuID;
	}

	/**
	 * Initializes this state.
	 * 
	 * @param container
	 *            the GameContainer to be used
	 * @param game
	 *            the game registered to this state
	 * @throws SlickException
	 *             in case of trouble
	 */
	@Override
	public void init(final GameContainer container, final StateBasedGame game)
			throws SlickException {
		widgets = new ArrayList<Widget>();
		this.container = container;
	}

	/*
	 * (non-Javadoc)
	 * @see org.newdawn.slick.state.BasicGameState#keyReleased(int, char)
	 */
	@Override
	public void keyReleased(final int key, final char keyCode) {
		super.keyReleased(key, keyCode);
		if (key == Input.KEY_UP) {
			--focus;
			calculateFocus();
		} else if (key == Input.KEY_DOWN) {
			++focus;
			calculateFocus();
		}
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * org.newdawn.slick.state.BasicGameState#leave(org.newdawn.slick.GameContainer
	 * , org.newdawn.slick.state.StateBasedGame)
	 */
	@Override
	public void leave(final GameContainer container, final StateBasedGame game)
			throws SlickException {
		super.leave(container, game);
		widgets.clear();
		calculateFocus();
		setRootPane(createRootPane());
		calculateFocus();
	}

	/**
	 * Make focusable.
	 * 
	 * @param widget
	 *            the widget
	 */
	public void makeFocusable(final Widget widget) {
		if (widgets.contains(widget)) {
			widgets.remove(widget);
		}
		widgets.add(widget);
	}

	/**
	 * Make unfocusable.
	 * 
	 * @param widget
	 *            the widget
	 */
	public void makeUnfocusable(final Widget widget) {
		widgets.remove(widget);
	}
	/**
	 * Renders this state.
	 * 
	 * @param container
	 *            the GameContainer to be used
	 * @param game
	 *            the game registered to this state
	 * @param graphics
	 *            use this graphics context to render the state
	 * @throws SlickException
	 *             in case of trouble
	 */
	@Override
	public void render(final GameContainer container, final StateBasedGame game,
			final Graphics graphics) throws SlickException {
		if (background != null) {
			graphics.drawImage(background, 0, 0);
		}
	}

	/**
	 * Sets the background image.
	 * 
	 * @param background
	 *            the new background image
	 */
	public void setBackground(final Image background) {
		this.background = background;
	}

	/**
	 * Updates this state.
	 * 
	 * @param container
	 *            the GameContainer to be used
	 * @param game
	 *            the game registered to this state
	 * @param delta
	 *            the time since the last call in milliseconds
	 * @throws SlickException
	 *             tin case of trouble
	 */
	@Override
	public void update(final GameContainer container, final StateBasedGame game, final int delta)
			throws SlickException {
		if (!container.hasFocus()) {
			return;
		}
	}

	@Override
	protected RootPane createRootPane() {
		widgets.clear();
		calculateFocus();
		return super.createRootPane();
	}

	/**
	 * Calculate focus.
	 */
	private void calculateFocus() {
		if (widgets.isEmpty()) {
			focus = 0;
		} else {
			focus = (widgets.size() + focus) % widgets.size();
			widgets.get(focus).requestKeyboardFocus();
		}
	}
}
